# -*- coding: utf-8 -*-
"""
作者: liuzaiqiang
邮箱: zaiqiangliu@163.com
地址: 新疆大学
创建时间: 2025/11/14 22:01
功能描述:

题目描述
    给定一个按学生成绩（升序）排列的列表 students，每个元素是元组 (name, score)（姓名为字符串，成绩为整数）。请编写函数 find_first_pass，接收该列表和及格线 pass_score，返回第一个成绩大于等于及格线的学生信息（元组）。若所有学生均不及格，返回 None。

要求：
    必须使用二分查找算法
    必须通过 key 函数提取比较依据（体现其意义）

"""

from typing import List, Tuple, Optional, Callable


def find_first_pass_asc(students: List[Tuple[str, int]],
                        pass_score: int,
                        key: Callable[[Tuple[str, int]], int] = lambda x: x[1]) -> Optional[Tuple[str, int]]:
    """
    在按成绩升序排列的学生列表中，找到第一个成绩>=及格线的学生

    :param students: 按成绩升序排列的学生列表（从低到高）
    :param pass_score: 及格线分数
    :param key: 提取成绩的函数（默认取元组第二个元素）
    :return: 第一个及格的学生信息或None
    """
    low, high = 0, len(students) - 1
    result_index = -1  # 未找到时返回None

    while low <= high:
        mid = low + (high - low) // 2
        # 通过key函数提取成绩（核心：与元素结构解耦）
        mid_score = key(students[mid])

        if mid_score >= pass_score:
            result_index = mid
            high = mid - 1
        else:
            low = mid + 1

    return students[result_index] if result_index != -1 else None


# 测试代码（升序排列的学生列表）
if __name__ == '__main__':
    # 按成绩升序排列组织一批数据（分数从低到高）
    students_asc = [
        ("孙八", 45),
        ("钱七", 59),
        ("赵六", 63),
        ("王五", 76),
        ("李四", 88),
        ("张三", 95)
    ]
    test_scores = [60, 63, 70, 90, 100, 40]
    for score in test_scores:
        #result = find_first_pass_asc(students_asc, score)  这个写法可以
        # 调用时显式传入key参数（提取成绩）
        result = find_first_pass_asc(students_asc, score, key=lambda x: x[1])
        print(f"及格线 {score} 分：{result}")


"""
#通过key函数提取成绩（核心：与元素结构解耦）
mid_score = key(students[mid])
如何理解：

“解耦” 在这里的意思是：让二分查找的核心逻辑（比较、定位）与列表元素的具体结构（比如是元组、字典还是对象）分离开来，彼此不依赖。
key 函数正是通过 “中间转换” 实现了这种分离。
举个例子

场景 1：没有 key 函数（耦合状态）
假设学生信息是元组 (name, score)，如果不使用 key 函数，代码可能会直接写死提取成绩的逻辑：
#直接依赖元素结构（元组的第二个元素）
mid_score = students[mid][1]  # 硬编码了“元组索引1是成绩”
这会导致： 
如果学生信息改成字典 {"name": "张三", "score": 95}，必须修改这行代码为 mid_score = students[mid]["score"]。
如果改成类对象 Student(name="张三", score=95)，又得改成 mid_score = students[mid].score。
核心逻辑与元素结构强耦合，改结构就必须改逻辑，非常不灵活。

场景 2：有 key 函数（解耦状态）
当使用 key 函数时：
#不依赖元素结构，通过key函数转换
mid_score = key(students[mid])
此时：
如果元素是元组 (name, score)，传入 key=lambda x: x[1] 即可。
如果元素是字典 {"name":..., "score":...}，只需传入 key=lambda x: x["score"]。
如果元素是类对象，传入 key=lambda x: x.score 即可。

二分查找的核心逻辑（循环、比较、调整高低指针）完全不用改，只需要修改 key 函数的定义。

总结：key 函数的解耦作用
它相当于在 “元素结构” 和 “二分查找逻辑” 之间加了一层 “翻译器”：
    元素结构变化时，只需要更新 “翻译器”（key 函数）。
    二分查找的核心逻辑（比较、定位）完全不用关心元素是元组、字典还是对象，只管基于 key 函数返回的 “比较值”（比如成绩）进行操作。
这种分离就是 “解耦”，让代码更灵活、更容易维护。


"""